<template>
	<div class="ma-api-tree">
		<div class="ma-tree-toolbar">
			<div class="ma-tree-toolbar-search"><i class="ma-icon ma-icon-search"></i><input placeholder="输入关键字搜索"
																							 @input="e => doSearch(e.target.value)"/>
			</div>
			<div>
				<div class="ma-tree-toolbar-btn" title="新建分组" @click="openCreateGroupModal()">
					<i class="ma-icon ma-icon-tianjiawenjianjia"></i>
				</div>
				<div class="ma-tree-toolbar-btn" title="刷新函数" @click="initData()">
					<i class="ma-icon ma-icon-shuaxin"></i>
				</div>
				<div class="ma-tree-toolbar-btn" title="折叠" @click="rebuildTree(true)">
					<i class="ma-icon ma-icon-folding"></i>
				</div>
				<div v-show="treeSort === false" class="ma-tree-toolbar-btn" title="当前为字母降序" @click="treeSortHandle()">
					<i class="ma-icon ma-icon-descending"></i>
				</div>
				<div v-show="treeSort !== false" class="ma-tree-toolbar-btn" title="当前为字母升序" @click="treeSortHandle()">
					<i class="ma-icon ma-icon-ascending"></i>
				</div>
			</div>
		</div>
		<magic-tree :data="tree" :forceUpdate="forceUpdate">
			<template #folder="{ item }">
				<div
						class="ma-tree-item-header ma-tree-hover"
						:class="{ 'ma-tree-select': item.selectRightItem }"
						:style="{ 'padding-left': 17 * item.level + 'px' }"
						v-if="item._searchShow !== false"
						@click="$set(item, 'opened', !item.opened)"
						@contextmenu.prevent="e => folderRightClickHandle(e, item)"
						:draggable="true"
						@dragstart.stop="e => draggable(item, e, 'dragstart')"
						@dragenter="e => draggable(item, e, 'dragenter')"
						@dragend.stop="e => draggable(item, e, 'dragend')"
						@dragover.prevent
				>
					<i class="ma-icon" :class="item.opened ? 'ma-icon-arrow-bottom' : 'ma-icon-arrow-right'"></i>
					<i class="ma-icon ma-icon-list"></i>
					<label>{{ item.name }}</label>
					<span>({{ item.path }})</span>
				</div>
			</template>
			<template #file="{ item }">
				<div
						class="ma-tree-hover"
						:class="{ 'ma-tree-select': item.selectRightItem || item._id === currentFileItem._id }"
						v-if="item._searchShow !== false"
						:style="{ 'padding-left': 17 * item.level + 'px' }"
						@click="open(item)"
						@contextmenu.prevent="e => fileRightClickHandle(e, item)"
						:draggable="true"
						@dragstart.stop="e => draggable(item, e, 'dragstart')"
						@dragenter="e => draggable(item, e, 'dragenter')"
						@dragend.stop="e => draggable(item, e, 'dragend')"
						@dragover.prevent
				>
					<i class="ma-icon ma-icon-script"></i>
					<label>{{ item.name }}</label>
					<span>({{ item.path }})</span>
				</div>
			</template>
		</magic-tree>
		<magic-dialog v-model="createGroupObj.visible" align="right"
					  :title="createGroupObj.id ? '修改分组:' + createGroupObj.tmpName : '创建分组'"
					  @onClose="createGroupAction(false)">
			<template #content>
				<label>分组名称：</label>
				<magic-input v-model="createGroupObj.name"/>
				<div style="height: 2px"></div>
				<label>分组路径：</label>
				<magic-input v-model="createGroupObj.path"/>
			</template>
			<template #buttons>
				<button class="active" @click="createGroupAction(true)">{{ createGroupObj.id ? '修改' : '创建' }}</button>
				<button @click="createGroupAction(false)">取消</button>
			</template>
		</magic-dialog>
	</div>
</template>

<script>
    import bus from '@/scripts/bus.js'
    import MagicTree from '@/components/common/magic-tree.vue'
    import request from '@/api/request.js'
    import MagicDialog from '@/components/common/modal/magic-dialog.vue'
    import MagicInput from '@/components/common/magic-input.vue'
    import {replaceURL} from '@/scripts/utils.js'
    import JavaClass from '@/scripts/editor/java-class.js'
    import Key from '@/scripts/hotkey.js'

    export default {
        name: 'MagicFunctionList',
        props: {
            groups: Array
        },
        components: {
            MagicTree,
            MagicDialog,
            MagicInput
        },
        data() {
            return {
                bus: bus,
                // 分组list数据
                listGroupData: [],
                // 接口list数据
                listChildrenData: [],
                // 分组+接口tree数据
                tree: [],
                // 数据排序规则,true:升序,false:降序
                treeSort: true,
                // 新建分组对象
                createGroupObj: {
                    visible: false,
                    id: '',
                    name: '',
                    path: '',
                    parentId: '',
                    type: '2',
                    children: []
                },
                // 换成一个临时对象，修改使用
                tempGroupObj: {},
                // 当前打开的文件
                currentFileItem: {},
                // 绑定给magic-tree组件，用来触发子组件强制更新
                forceUpdate: true,
                // 拖拽的item
                draggableItem: {},
                draggableTargetItem: {}
            }
        },
        methods: {
            doSearch(keyword) {
                let loopSearch = (row, parentName, parentPath) => {
                    if (row.folder) {
                        row.children.forEach(it => loopSearch(it, parentName + '/' + (row.name || ''), parentPath + '/' + (row.path || '')))
                        row._searchShow = row.children.some(it => it._searchShow)
                    } else {
                        row._searchShow = replaceURL(parentName + '/' + (row.name || '')).indexOf(keyword) > -1 || replaceURL(parentPath + '/' + (row.path || '')).indexOf(keyword) > -1
                    }
                }
                this.tree.forEach(it => loopSearch(it, '', ''))
                this.changeForceUpdate()
            },
            doFindFunction(path) {
                let finds = this.listChildrenData.filter(it => !it.folder && replaceURL(it.groupPath + '/' + it.path) === path)
                return finds.length > 0 ? finds[0] : null;
            },
            open(item) {
                bus.$emit('open', item)
                this.currentFileItem = item
            },
            // 初始化数据
            initData() {
                this.tree = []
                request.send('group/list?type=2').success(data => {
                    this.listGroupData = data;
                    request.send('function/list').success(data => {
                        this.listChildrenData = data
                        this.initTreeData()
                    })
                })
            },
            // 初始化tree结构数据
            initTreeData() {
                // 1.把所有的分组id存map,方便接口列表放入,root为没有分组的接口
                let groupItem = {root: []}
                this.listGroupData.forEach(element => {
                    groupItem[element.id] = []
                    element.folder = true
                    this.$set(element, 'opened', true)
                    // 缓存一个name和path给后面使用
                    element.tmpName = element.name.indexOf('/') === 0 ? element.name : '/' + element.name
                    element.tmpPath = element.path.indexOf('/') === 0 ? element.path : '/' + element.path
                })
                // 2.把所有的接口列表放入分组的children
                this.listChildrenData.forEach((element, index) => {
                    element._id = element.id
                    element._type = 'function'
                    if (groupItem[element.groupId]) {
                        groupItem[element.groupId].push(element)
                    } else {
                        element.groupName = ''
                        element.groupPath = ''
                        groupItem['root'].push(element)
                    }
                })
                // 3.将分组列表变成tree,并放入接口列表,分组在前,接口在后
                let arrayToTree = (arr, parentItem, groupName, groupPath, level) => {
                    //  arr 是返回的数据  parendId 父id
                    let temp = []
                    let treeArr = arr
                    treeArr.forEach((item, index) => {
                        if (item.parentId == parentItem.id) {
                            item.level = level
                            item.tmpName = groupName + item.tmpName
                            item.tmpPath = groupPath + item.tmpPath
                            // 递归调用此函数
                            item.children = arrayToTree(treeArr, item, item.tmpName, item.tmpPath, level + 1)
                            if (groupItem[item.id]) {
                                groupItem[item.id].forEach(element => {
                                    element.level = item.level + 1
                                    element.groupName = item.tmpName
                                    element.groupPath = item.tmpPath
                                    element.groupId = item.id
                                    this.$set(item.children, item.children.length, element)
                                })
                            }
                            this.$set(temp, temp.length, treeArr[index])
                        }
                    })
                    return temp
                }
                this.tree = [...arrayToTree(this.listGroupData, {id: 0}, '', '', 0), ...groupItem['root']]
                this.sortTree()
            },
            // 重新构建tree的path和name,第一个参数表示是否全部折叠
            rebuildTree(folding) {
                let buildHandle = (arr, parentItem, level) => {
                    arr.forEach(element => {
                        element.level = level
                        // 处理分组
                        if (element.folder === true) {
                            element.tmpName = (parentItem.tmpName + '/' + element.name).replace(new RegExp('(/)+', 'gm'), '/')
                            element.tmpPath = (parentItem.tmpPath + '/' + element.path).replace(new RegExp('(/)+', 'gm'), '/')
                            if (folding === true) {
                                this.$set(element, 'opened', false)
                            }
                            if (element.children && element.children.length > 0) {
                                buildHandle(element.children, element, level + 1)
                            }
                        } else {
                            // 处理接口
                            element.groupName = parentItem.tmpName
                            element.groupPath = parentItem.tmpPath
                            element.groupId = parentItem.id
                        }
                    })
                }
                buildHandle(this.tree, {tmpName: '', tmpPath: ''}, 0)
                if (this.currentFileItem._id) {
                    this.open(this.currentFileItem)
                }
                this.sortTree()
            },
            treeSortHandle(flag) {
                this.treeSort = !this.treeSort
                this.sortTree()
            },
            // 排序tree,分组在前,接口在后
            sortTree() {
                if (this.treeSort === null) {
                    return
                }
                let sortItem = function (item1, item2) {
                    return item1.name.localeCompare(item2.name, 'zh-CN')
                }
                let sortHandle = arr => {
                    // 分组
                    let folderArr = []
                    // 接口
                    let fileArr = []
                    arr.forEach(element => {
                        if (element.folder === true) {
                            if (element.children && element.children.length > 0) {
                                element.children = sortHandle(element.children)
                            }
                            folderArr.push(element)
                        } else {
                            fileArr.push(element)
                        }
                    })
                    folderArr.sort(sortItem)
                    fileArr.sort(sortItem)
                    if (this.treeSort === false) {
                        folderArr.reverse()
                        fileArr.reverse()
                    }
                    return folderArr.concat(fileArr)
                }
                this.tree = sortHandle(this.tree)
                this.changeForceUpdate()
            },
            // 文件夹右键菜单
            folderRightClickHandle(event, item) {
                this.$set(item, 'selectRightItem', true)
                this.$magicContextmenu({
                    menus: [
                        {
                            label: '新建函数',
                            onClick: () => {
                                let newItem = {
                                    id: '',
                                    _id: new Date().getTime() + '' + Math.floor(Math.random() * 1000),
                                    _type: 'function',
                                    path: '',
                                    script: null,
                                    name: '未定义名称',
                                    parameter: null,
                                    description: null,
                                    level: item.level + 1,
                                    groupName: item.tmpName,
                                    groupPath: item.tmpPath,
                                    groupId: item.id
                                }
                                this.pushFileItemToGroup(this.tree, newItem)
                                this.open(newItem)
                            }
                        },
                        {
                            label: '刷新函数',
                            divided: true,
                            onClick: () => {
                                this.initData()
                            }
                        },
                        {
                            label: '新建分组(Alt+G)',
                            onClick: () => {
                                this.openCreateGroupModal(null, item)
                            }
                        },
                        {
                            label: '修改分组',
                            onClick: () => {
                                this.openCreateGroupModal(item)
                            }
                        },
                        {
                            label: '删除分组',
                            divided: true,
                            onClick: () => {
                                this.deleteGroupAction(item)
                            }
                        },
                        {
                            label: '移动到根节点',
                            onClick: () => {
                                item.parentId = '0'
                                request.send('group/update', item).success(data => {
                                    bus.$emit('report', 'group_update')
                                    // 先删除移动前的分组
                                    this.deleteOrAddGroupToTree(this.tree, item, true)
                                    // 再把移动后的分组放进去
                                    this.deleteOrAddGroupToTree(this.tree, item)
                                    this.rebuildTree()
                                    this.initCreateGroupObj()
                                    this.changeForceUpdate()
                                })
                            }
                        }
                    ],
                    event,
                    zIndex: 9999,
                    destroy: () => {
                        this.$set(item, 'selectRightItem', false)
                    }
                })
                return false
            },
            // 文件右键菜单
            fileRightClickHandle(event, item) {
                this.$set(item, 'selectRightItem', true)
                this.$magicContextmenu({
                    menus: [
                        {
                            label: '复制函数',
                            onClick: () => {
                                if (!item.id) {
                                    this.$magicAlert({content: '请先保存在复制！'})
                                    return
                                }
                                let newItem = {
                                    copy: true,
                                    ...item
                                }
                                newItem.name = newItem.name + '(复制)'
                                newItem._id = new Date().getTime() + '' + Math.floor(Math.random() * 1000)
                                newItem.selectRightItem = false
                                this.pushFileItemToGroup(this.tree, newItem)
                                this.open(newItem)
                            }
                        },
                        {
                            label: '刷新函数',
                            onClick: () => {
                                this.initData()
                            }
                        },
                        {
                            label: '删除函数',
                            divided: true,
                            onClick: () => {
                                this.deleteApiInfo(item)
                            }
                        }
                    ],
                    event,
                    zIndex: 9999,
                    destroy: () => {
                        this.$set(item, 'selectRightItem', false)
                    }
                })
                return false
            },
            // 删除接口
            deleteApiInfo(item) {
                bus.$emit('status', `准备删除函数「${item.name}(${item.path})」`)
                this.$magicConfirm({
                    title: '删除函数',
                    content: `是否要删除函数「${item.name}(${item.path})」`,
                    onOk: () => {
                        if (item.id) {
                            request.send('function/delete', {id: item.id}).success(data => {
                                if (data) {
                                    bus.$emit('status', `函数「${item.name}(${item.path})」已删除`)
                                    bus.$emit('report', 'function_delete')
                                    item['delete'] = true
                                    this.open(item)
                                    this.deleteOrAddGroupToTree(this.tree, item, true)
                                } else {
                                    this.$magicAlert({content: '删除失败'})
                                }
                            })
                        } else {
                            item['delete'] = true
                            this.open(item)
                            this.deleteOrAddGroupToTree(this.tree, item, true)
                        }
                    }
                })
            },
            // 打开新建分组弹窗
            openCreateGroupModal(item, parentItem) {
                if (item) {
                    for (const key in this.createGroupObj) {
                        this.createGroupObj[key] = item[key]
                    }
                    this.createGroupObj.tmpName = this.createGroupObj.name
                    this.tempGroupObj = item
                }
                if (parentItem) {
                    this.createGroupObj.parentId = parentItem.id
                }
                this.createGroupObj.visible = true
            },
            // 保存|修改分组
            createGroupAction(flag) {
                if (flag === true) {
                    // 验证数据
                    if (!this.createGroupObj.name) {
                        this.$magicAlert({content: '分组名称不能为空'})
                        return false
                    }
                    // id存在发送更新请求，不存在发送新增请求
                    if (this.createGroupObj.id) {
                        request.send('group/update', this.createGroupObj).success(data => {
                            bus.$emit('report', 'group_update')
                            this.tempGroupObj.name = this.createGroupObj.name
                            this.tempGroupObj.path = this.createGroupObj.path
                            this.rebuildTree()
                            this.initCreateGroupObj()
                            this.tempGroupObj = {}
                        })
                    } else {
                        request.send('group/create', this.createGroupObj).success(data => {
                            this.createGroupObj.id = data
                            this.createGroupObj.folder = true
                            bus.$emit('report', 'group_create')
                            bus.$emit('status', `分组「${this.createGroupObj.name}」创建成功`)
                            this.deleteOrAddGroupToTree(this.tree, this.createGroupObj)
                            this.rebuildTree()
                            this.initCreateGroupObj()
                        })
                    }
                } else {
                    this.initCreateGroupObj()
                }
            },
            // 初始化createGroupObj对象
            initCreateGroupObj() {
                this.createGroupObj = {
                    visible: false,
                    id: '',
                    name: '',
                    path: '',
                    parentId: '',
                    type: '2',
                    children: []
                }
            },
            // 删除分组
            deleteGroupAction(item) {
                bus.$emit('status', `准备删除接口分组「${item.name}」`)
                this.$magicConfirm({
                    title: '删除函数分组',
                    content: `是否要删除函数分组「${item.name}」`,
                    onOk: () => {
                        request.send('group/delete', {groupId: item.id}).success(data => {
                            if (data) {
                                bus.$emit('report', 'group_delete')
                                bus.$emit('status', `函数分组「${item.name}」已删除`)
                                // 递归通知编辑页面关闭tab
                                let noticeCloseTab = arr => {
                                    arr.forEach(element => {
                                        if (element.folder !== true) {
                                            element['delete'] = true
                                            this.open(element)
                                        } else if (element.children && element.children.length > 0) {
                                            noticeCloseTab(element.children)
                                        }
                                    })
                                }
                                noticeCloseTab(item.children || [])
                                this.deleteOrAddGroupToTree(this.tree, item, true)
                            } else {
                                this.$magicAlert({content: '删除失败'})
                            }
                        })
                    }
                })
            },
            // 将文件类型的对象，放入到点击的同级
            pushFileItemToGroup(tree, newItem) {
                // 标记是否找到对应的item，找到了就退出递归
                let find = false
                for (const index in tree) {
                    const element = tree[index]
                    // 排除分组类型
                    if (element.folder === true && element.id === newItem.groupId) {
                        this.$set(element.children, element.children.length, newItem)
                        this.changeForceUpdate()
                        return true
                    } else if (element.children && element.children.length > 0) {
                        find = this.pushFileItemToGroup(element.children, newItem)
                    }
                    if (find === true) {
                        return true
                    }
                }
            },
            // 处理删除和添加分组
            deleteOrAddGroupToTree(tree, item, flag) {
                // 如果是添加到根目录
                if (flag !== true && item.folder === true && (!item.parentId || item.parentId === '0')) {
                    item.level = 0
                    item.tmpName = ('/' + item.name).replace(new RegExp('(/)+', 'gm'), '/')
                    item.tmpPath = ('/' + item.path).replace(new RegExp('(/)+', 'gm'), '/')
                    item.folder = true
                    this.$set(item, 'opened', true)
                    this.$set(item, 'selectRightItem', false)
                    tree.push(item)
                    return true
                }
                // 标记是否找到对应的item，找到了就退出递归
                let find = false
                for (const index in tree) {
                    const element = tree[index]
                    // 分组
                    if (item.folder === true) {
                        // 排除分组类型
                        if (flag === true && element.folder === true && element.id === item.id) {
                            // 删除
                            tree.splice(index, 1)
                            find = true
                            this.changeForceUpdate()
                        } else if (flag !== true && element.folder === true && element.id === item.parentId) {
                            // 新增
                            item.level = element.level + 1
                            item.tmpName = (element.tmpName + '/' + item.name).replace(new RegExp('(/)+', 'gm'), '/')
                            item.tmpPath = (element.tmpPath + '/' + item.path).replace(new RegExp('(/)+', 'gm'), '/')
                            item.folder = true
                            this.$set(item, 'opened', true)
                            this.$set(item, 'selectRightItem', false)
                            element.children.push(item)
                            find = true
                            this.changeForceUpdate()
                        } else if (element.children && element.children.length > 0) {
                            find = this.deleteOrAddGroupToTree(element.children, item, flag)
                        }
                    } else {
                        // 接口
                        if (flag === true && element.folder !== true && element.id === item.id) {
                            // 删除
                            tree.splice(index, 1)
                            find = true
                            this.changeForceUpdate()
                        } else if (flag !== true && element.folder === true && element.id === item.groupId) {
                            // 新增
                            item.level = element.level + 1
                            item.groupName = element.tmpName
                            item.groupPath = element.tmpPath
                            element.children.push(item)
                            find = true
                            this.changeForceUpdate()
                        } else if (element.children && element.children.length > 0) {
                            find = this.deleteOrAddGroupToTree(element.children, item, flag)
                        }
                    }
                    if (find === true) {
                        return true
                    }
                }
            },
            // 强制触发子组件更新
            changeForceUpdate() {
                this.forceUpdate = !this.forceUpdate
            },
            draggable(item, event, type) {
                switch (type) {
                    // 开始拖拽
                    case 'dragstart':
                        this.draggableItem = item
                        break
                    // 拖拽到某个元素上
                    case 'dragenter':
                        this.draggableTargetItem = item
                        break
                    // 结束拖拽
                    case 'dragend':
                        // 目标对象必须是分组
                        if (this.draggableTargetItem.folder === true) {
                            // 移动分组
                            if (this.draggableItem.folder === true && this.draggableItem.parentId !== this.draggableTargetItem.id && this.draggableItem.id !== this.draggableTargetItem.id) {
                                // 检测移动的目录是否是自己的子目录
                                let checkChildrenFolder = arr => {
                                    let flag = arr.some(item => item.id === this.draggableTargetItem.id)
                                    if (flag) {
                                        return flag
                                    }
                                    for (let i = 0; i < arr.length; i++) {
                                        if (arr[i].children && arr[i].children.length > 0) {
                                            if (checkChildrenFolder(arr[i].children || [])) {
                                                return true
                                            }
                                        }
                                    }
                                    return false
                                }
                                if (checkChildrenFolder(this.draggableItem.children) === false) {
                                    let params = JSON.parse(JSON.stringify(this.draggableItem))
                                    params.parentId = this.draggableTargetItem.id
                                    request.send('group/update', params).success(data => {
                                        bus.$emit('report', 'group_update')
                                        // 先删除移动前的分组
                                        this.deleteOrAddGroupToTree(this.tree, this.draggableItem, true)
                                        // 再把移动后的分组放进去
                                        this.deleteOrAddGroupToTree(this.tree, params)
                                        this.rebuildTree()
                                        this.initCreateGroupObj()
                                        this.changeForceUpdate()
                                    })
                                } else {
                                    this.$magicAlert({content: `不能移到${this.draggableTargetItem.name}`})
                                }
                            } else if (this.draggableItem.parentId !== this.draggableTargetItem.id) {
                                // 移动接口
                                // 接口不能在目标分组的第一级children里
                                if (this.draggableTargetItem.children.some(item => item.id === this.draggableItem.id) === false) {
                                    request.send('function/move', {
                                        id: this.draggableItem.id,
                                        groupId: this.draggableTargetItem.id
                                    }).success(data => {
                                        // 先删除移动前的接口
                                        this.deleteOrAddGroupToTree(this.tree, this.draggableItem, true)
                                        // 再把移动后的接口放进去
                                        this.draggableItem.groupId = this.draggableTargetItem.id
                                        this.deleteOrAddGroupToTree(this.tree, this.draggableItem)
                                        this.rebuildTree()
                                        this.initCreateGroupObj()
                                        this.changeForceUpdate()
                                    })
                                }
                            }
                        }
                        break
                }
            }
        },
        mounted() {
            JavaClass.setupOnlineFunction(this.doFindFunction);
            this.bus.$on('opened', item => {
                this.currentFileItem = item
            })
            this.bus.$on('delete-api', item => {
                this.deleteApiInfo(item)
            })
            let element = document.getElementsByClassName('ma-container')[0]
            // 新建分组快捷键
            Key.bind(element, Key.Alt | Key.G, () => this.openCreateGroupModal())
        }
    }
</script>

<style>
	@import './magic-resource.css';
</style>
